39  控制结构

39.1 引言程序控制流的理论基础

控制流(Control Flow)是程序设计的核心,决定了代码的执行顺序和逻辑分支。

理论背景:控制流图(Flowchart)

程序的三种基本控制结构(Bohm-Jacopini定理): 1. 顺序结构(Sequence): 语句按顺序依次执行 2. 选择结构(Selection): 根据条件选择执行路径(if-else) 3. 循环结构(Iteration): 重复执行某段代码(for/while)

数学基础:图论

控制流可以表示为有向图(Directed Graph): - 节点(Node): 表示程序语句 - 边(Edge): 表示控制流转移 - 路径(Path): 从入口到出口的执行路径

金融应用: - 交易策略: 条件判断买入/卖出 - 风险控制: 止损/止盈逻辑 - 批量处理: 遍历股票列表 - 迭代优化: 梯度下降法求解

39.2 if-else选择结构

39.2.1 条件表达式

理论背景:布尔逻辑与谓词逻辑

布尔(Boolean)代数由乔治·布尔于1854年创立,是现代计算机科学的基础。

三种基本运算: 1. 与(AND): A and B - 两者都真才真 2. 或(OR): A or B - 至少一个真就真 3. 非(NOT): not A - 真变假,假变真

短路求值(Short-circuit Evaluation):

A and B  # 如果A为假,不计算B
A or B   # 如果A为真,不计算B

金融应用: 风险管理中的多层条件判断

列表 39.1
# =============================================================================
# 题目:实现个人所得税累进税率计算系统
# =============================================================================
# 本任务演示如何使用if-elif-else结构实现累进税率计算
# 累进税率是现代税收制度的核心,体现"量能课税"原则
# 金融应用:薪酬管理、税务筹划、个人所得税申报

# ==================== 定义税收计算函数 ====================
def calculate_tax(salary):
    """
    计算个人所得税(简化版)

    原理:中国个人所得税采用7级超额累进税率
    - 超额累进:只对超过该级的部分征税
    - 速算扣除数:简化计算,避免分段计算

    参数:
        salary (float): 税前工资,单位:元

    返回:
        tuple: (tax, after_tax)
            tax (float): 应缴税额
            after_tax (float): 税后工资
    """
    # ==================== 第一步:确定适用税率档次 ====================
    # 使用if-elif-else结构匹配累进税率表
    # 从低到高依次判断,一旦匹配就跳出(互斥关系)

    # 第1级:月收入 <= 3000元
    if salary <= 3000:
        # 税率:0%(免税)
        rate = 0  # 税率
        deduction = 0  # 速算扣除数

    # 第2级:3000 < 月收入 <= 12000
    elif salary <= 12000:
        # 税率:3%
        rate = 0.03  # 适用税率
        deduction = 0  # 速算扣除数(本级无需扣除)

    # 第3级:12000 < 月收入 <= 25000
    elif salary <= 25000:
        # 税率:10%
        rate = 0.10  # 适用税率
        deduction = 210  # 速算扣除数(预扣除的税额)

    # 第4级:25000 < 月收入 <= 35000
    elif salary <= 35000:
        # 税率:20%
        rate = 0.20  # 适用税率
        deduction = 1410  # 速算扣除数

    # 第5级:35000 < 月收入 <= 55000
    elif salary <= 55000:
        # 税率:25%
        rate = 0.25  # 适用税率
        deduction = 2660  # 速算扣除数

    # 第6级:55000 < 月收入 <= 80000
    elif salary <= 80000:
        # 税率:30%
        rate = 0.30  # 适用税率
        deduction = 4410  # 速算扣除数

    # 第7级:月收入 > 80000
    else:
        # 税率:35%(最高档)
        rate = 0.35  # 最高税率
        deduction = 7160  # 速算扣除数

    # ==================== 第二步:计算应纳税额 ====================
    # 应纳税所得额 = 税前工资 - 起征点
    taxable_income = salary - 5000  # 5000元是个税起征点(免征额)

    # 应纳税额 = 应纳税所得额 × 税率 - 速算扣除数
    tax = taxable_income * rate - deduction

    # 使用max()确保税额不为负(最低为0元)
    # 如果tax < 0,说明收入低于起征点,不需要纳税
    tax = max(0, tax)  # max(0, tax)返回两者中的较大值

    # ==================== 第三步:计算税后工资 ====================
    # 税后工资 = 税前工资 - 应纳税额
    after_tax = salary - tax

    # 返回税额和税后工资(元组形式)
    return tax, after_tax

# ==================== 测试案例:不同收入水平的税收 ====================
# 创建测试工资列表:覆盖各个税率档次
salaries = [2500, 5000, 15000, 30000, 60000, 100000]
# 解读:从免税档到最高档,全面测试税率计算

# 打印表头
print('工资扣税计算:')
# 打印分隔线(60个减号)
print('-' * 60)

# ==================== 遍历每个工资案例进行计算 ====================
# for循环:依次对列表中的每个工资进行税收计算
for salary in salaries:
    # 调用calculate_tax函数,返回税额和税后工资
    tax, after_tax = calculate_tax(salary)  # 元组解包

    # 计算实际税负率(税额/税前工资)
    # 三元表达式:如果salary > 0,计算税率,否则为0(避免除零错误)
    tax_rate = tax / salary * 100 if salary > 0 else 0

    # 格式化输出:使用f-string进行字符串格式化
    # {salary:8.2f}:宽度8,保留2位小数,右对齐
    # {tax:7.2f}:宽度7,保留2位小数
    # {tax_rate:5.2f}:宽度5,保留2位小数
    # {after_tax:8.2f}:宽度8,保留2位小数
    print(f'税前工资: {salary:8.2f}元 | 税额: {tax:7.2f}元 | 税率: {tax_rate:5.2f}% | 税后: {after_tax:8.2f}元')

代码深度解析:

  1. elif语句:
    • “else if”的缩写
    • 从上到下依次判断
    • 一旦某个条件为真,执行对应分支后跳出
  2. 累进税率:
    • 边际税率随收入增加
    • 高收入部分适用更高税率
    • 体现”量能课税”原则
  3. 起征点:
    • salary - 5000: 免税额
    • 低于5000元不纳税
    • 保护低收入群体

39.2.2 复杂条件判断

补充说明:嵌套if与逻辑运算

当需要同时满足多个条件时,有两种写法:

写法1: 嵌套if

if condition1:
    if condition2:
        # 执行代码

写法2: 逻辑运算

if condition1 and condition2:
    # 执行代码

选择建议: - 条件独立: 使用逻辑运算(and, or) - 条件相关: 使用嵌套if - 代码可读性优先

列表 39.2
# =============================================================================
# 题目:实现基于技术分析的交易信号生成系统
# =============================================================================
# 本任务演示嵌套if结构和多条件判断在量化交易中的应用
# 交易信号生成是量化交易系统的核心模块,需要综合多个技术指标
# 金融应用:算法交易、量化策略、风险管理系统

# ==================== 定义交易信号生成函数 ====================
def generate_signal(price, ma20, ma60, volume, avg_volume, rsi):
    """
    生成交易信号(基于技术指标的多条件判断)

    策略逻辑:
    1. 均线多头排列(MA20 > MA60)+ 价格突破 + 放量 + RSI未超买 → 买入
    2. 均线空头排列(MA20 < MA60)+ 价格跌破 + 放量 + RSI未超卖 → 卖出
    3. 其他情况 → 观望

    参数:
        price (float): 当前价格
        ma20 (float): 20日移动平均线
        ma60 (float): 60日移动平均线
        volume (float): 当日成交量
        avg_volume (float): 平均成交量
        rsi (float): 相对强弱指标(范围0-100)

    返回:
        tuple: (signal, reason)
            signal (str): 交易信号,'BUY'/'SELL'/'HOLD'
            reason (str): 信号原因说明
    """
    # ==================== 第一步:初始化信号状态 ====================
    signal = 'HOLD'  # 默认信号:观望(不交易)
    reason = []  # 原因列表:用于记录信号生成的逻辑依据

    # ==================== 第二步:判断市场趋势(第一层if) ====================
    # 条件1:均线多头排列(短期均线 > 长期均线)
    # 这是上升趋势的标志,表明近期价格走势强于长期趋势
    if ma20 > ma60:
        # 记录第一个判断依据:均线多头
        reason.append('均线多头')  # append()向列表添加元素

        # ==================== 第三步:判断价格位置(第二层if - 嵌套) ====================
        # 子条件:价格突破20日均线
        # 这表明上涨动能强劲,可能是买入机会
        if price > ma20:
            # 记录第二个判断依据
            reason.append('突破20日均线')

            # ==================== 第四步:量价配合确认(第三层if - 深度嵌套) ====================
            # 三重条件判断(使用and逻辑运算符):
            # 条件A:成交量 > 平均成交量的1.2倍(放量确认)
            # 条件B:RSI < 70(未超买,避免追高)
            # and运算符:两个条件都为True时,整体为True
            if volume > avg_volume * 1.2 and rsi < 70:
                # 同时满足三个条件,发出买入信号
                signal = 'BUY'  # 修改信号为买入
                reason.append('放量上涨')  # 记录第三个判断依据

    # ==================== 第五步:判断下跌趋势(elif分支) ====================
    # 条件2:均线空头排列(短期均线 < 长期均线)
    # elif(else if的缩写):如果前面的if条件不满足,判断本条件
    elif ma20 < ma60:
        # 记录第一个判断依据:均线空头
        reason.append('均线空头')

        # 子条件:价格跌破20日均线
        if price < ma20:
            # 记录第二个判断依据
            reason.append('跌破20日均线')

            # 三重条件判断:
            # 条件A:成交量 > 平均成交量的1.2倍(放量确认)
            # 条件B:RSI > 30(未超卖,避免在恐慌中卖出)
            if volume > avg_volume * 1.2 and rsi > 30:
                # 同时满足三个条件,发出卖出信号
                signal = 'SELL'  # 修改信号为卖出
                reason.append('放量下跌')  # 记录第三个判断依据

    # ==================== 第六步:震荡市场判断(else分支) ====================
    # 条件3:均线纠缠(MA20 ≈ MA60)
    # else:如果前面的if和elif条件都不满足,执行本分支
    else:
        # 市场处于震荡期,趋势不明朗,建议观望
        reason.append('均线纠缠,观望')

    # ==================== 第七步:返回结果 ====================
    # 将原因列表用分号连接成字符串
    # '; '.join(reason)将['a', 'b', 'c']转换为'a; b; c'
    return signal, '; '.join(reason)

# ==================== 测试案例:不同市场情景 ====================
# 创建测试数据列表(元组形式)
# 每个元组包含6个参数:(price, ma20, ma60, volume, avg_volume, rsi)
signals = [
    # 案例1:满足买入条件(均线多头+突破+放量+RSI未超买)
    (18.5, 18.0, 17.5, 2500000, 2000000, 45),  # 预期:BUY

    # 案例2:均线多头但量能不足(不满足放量条件)
    (18.5, 18.0, 17.5, 1500000, 2000000, 65),  # 预期:HOLD

    # 案例3:均线多头+突破+放量但RSI超买(风险较高)
    (18.5, 18.0, 17.5, 2500000, 2000000, 75),  # 预期:HOLD

    # 案例4:满足卖出条件(均线空头+跌破+放量)
    (16.5, 18.0, 17.5, 2500000, 2000000, 25),  # 预期:SELL
]

# 打印表头
print('交易信号分析:')
# 打印分隔线(80个等号)
print('-' * 80)

# ==================== 遍历每个测试案例 ====================
# for循环:依次处理每个市场情景
# 元组解包:将每个元组的6个值分别赋给对应的变量
for price, ma20, ma60, volume, avg_vol, rsi in signals:
    # 调用generate_signal函数生成交易信号
    signal, reason = generate_signal(price, ma20, ma60, volume, avg_vol, rsi)

    # 格式化输出:显示市场数据和对应的交易信号
    # {price:.1f}:保留1位小数
    print(f'价格={price:.1f}, MA20={ma20:.1f}, MA60={ma60:.1f}, 信号={signal}, 原因=[{reason}]')

39.3 循环结构

39.3.1 for循环

理论背景:迭代的数学定义

迭代(Iteration)是指重复执行某个过程,每次迭代都使结果更接近目标。

收敛性(Convergence): \[ \lim_{n \to \infty} |x_{n+1} - x_n| = 0 \]

金融应用: - 数值方法: 牛顿法求根、梯度下降 - 迭代优化: 投资组合优化 - 模拟仿真: 蒙特卡洛模拟

列表 39.3
# =============================================================================
# 题目:演示for循环在金融计算中的应用
# =============================================================================
# for循环是Python中最常用的迭代结构,用于遍历序列中的元素
# 本任务包含两个案例:复利计算和波动率计算
# 金融应用:投资回报计算、风险度量、时间序列分析

import numpy as np  # 导入NumPy库,用于数值计算

# ==================== 案例1:复利终值计算 ====================
# 复利是金融学的核心概念:利息产生利息
# 公式:FV = PV × (1 + r)^n
# 其中FV是终值,PV是现值,r是利率,n是期数

# 定义初始参数
principal = 10000  # 本金:初始投资金额(元)
rate = 0.05       # 年利率:5%(0.05表示5%)
years = 10        # 投资年限:10年

# 打印表头
print('复利增长过程:')
# 打印分隔线(50个减号)
print('-' * 50)
# 打印列标题:使用格式化字符串对齐列
# {<6}:左对齐,宽度6个字符
print(f'{"年份":<6}{"年初本金":<12}{"利息":<12}{"年末余额":<12}')
# 打印分隔线
print('-' * 50)

# ==================== for循环:逐期计算复利 ====================
# 初始化当前余额为本金
amount = principal  # amount表示每年的累计金额

# range(1, years + 1):生成从1到years的整数序列
# range(start, stop)生成从start到stop-1的整数(左闭右开区间)
# range(1, 11)生成[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for year in range(1, years + 1):
    # 计算当年利息:年初本金 × 利率
    interest = amount * rate  # 利息随余额增长而增长(复利的核心)

    # 计算年末余额:年初本金 + 当年利息
    # amount += interest 等价于 amount = amount + interest
    amount += interest  # 余额累积

    # 格式化输出:显示每年的增长过程
    # {amount - interest}:年初本金(年末余额 - 当年利息)
    # {interest}:当年利息
    # {amount}:年末余额
    print(f'{year:<6}{amount - interest:<12.2f}{interest:<12.2f}{amount:<12.2f}')

# ==================== 复利与单利对比分析 ====================
# 打印最终结果
print(f'\n复利终值: {amount:.2f}元')
# amount经过10年复利增长后的最终金额

# 计算单利终值作为对比
# 单利公式:FV = PV × (1 + r × n)
simple_interest = principal * (1 + rate * years)
print(f'单利对比: {simple_interest:.2f}元')
# 单利只对本金计算利息,不计算利息的利息

# 计算复利优势(复利 - 单利)
compound_advantage = amount - simple_interest
print(f'复利优势: {compound_advantage:.2f}元')
# 复利优势体现了"利息产生利息"的威力

# ==================== 案例2:年化波动率计算 ====================
# 波动率是金融风险的核心度量指标
# 年化波动率 = 日收益率标准差 × √252
# 其中252是一年的交易日数量

# 定义日收益率序列(10个交易日)
daily_returns = [0.02, -0.01, 0.03, -0.02, 0.01, 0.04, -0.03, 0.02, -0.01, 0.01]

# 打印原始数据
print(f'\n日收益率: {daily_returns}')
# 使用NumPy计算并显示统计摘要
print(f'日收益率均值: {np.mean(daily_returns):.4f}')
# np.mean():计算数组的平均值
print(f'日收益率标准差: {np.std(daily_returns):.4f}')
# np.std():计算数组的标准差(衡量波动程度)

# ==================== 手动计算标准差(深入理解) ====================
# 标准差公式:σ = √(Σ(xi - μ)² / (n-1))
# 其中μ是均值,n是样本数量

# 第一步:计算样本数量
n = len(daily_returns)  # len()返回列表长度

# 第二步:计算平均收益率
mean_return = sum(daily_returns) / n
# sum():列表求和;除以n得到平均值

# 第三步:计算方差(使用列表推导式)
# 列表推导式:[(r - mean_return) ** 2 for r in daily_returns]
# 语法:[表达式 for 变量 in 序列]
# 作用:对序列中每个元素应用表达式,生成新列表
variance = sum([(r - mean_return) ** 2 for r in daily_returns]) / (n - 1)
# 计算过程:
# 1. (r - mean_return):每个收益率与均值的偏差
# 2. (r - mean_return) ** 2:偏差的平方(消除负号)
# 3. sum(...):所有平方偏差之和
# 4. 除以(n-1):样本方差(使用n-1是无偏估计)

# 第四步:计算标准差(方差的平方根)
volatility_daily = np.sqrt(variance)
# np.sqrt():计算平方根

# 第五步:年化波动率
# 日波动率 × √252(252是一年交易日数)
volatility_annual = volatility_daily * np.sqrt(252)
# 年化波动率:将日波动率转换为年化水平

# ==================== 对比NumPy和手动计算 ====================
print(f'\n手动计算年化波动率: {volatility_annual:.4f}')
# 显示手动计算结果

print(f'NumPy计算年化波动率: {np.std(daily_returns) * np.sqrt(252):.4f}')
# 使用NumPy内置函数计算,验证结果一致性

代码深度解析:

  1. range()函数:

    • range(stop): 0到stop-1
    • range(start, stop): start到stop-1
    • range(start, stop, step): 自定义步长
  2. 列表推导式:

    [(r - mean_return) ** 2 for r in daily_returns]
    • 简洁的循环创建列表
    • 比显式循环快5-10倍
    • Pythonic的惯用法
  3. 复利公式: \[ FV = PV \times (1 + r)^n \]

    • FV: 终值(Future Value)
    • PV: 现值(Present Value)
    • r: 利率
    • n: 期数

39.3.2 while循环

理论背景:不动点迭代

while循环常用于不动点迭代(Fixed Point Iteration):

\[ x_{n+1} = g(x_n) \]

收敛条件: - 压缩映射: \(|g'(x)| < 1\) 对所有 \(x\) - 利普希茨条件: \(|g(x) - g(y)| \leq L |x - y|\), \(L < 1\)

金融应用: - 期权定价: 二叉树模型 - 方程求解: 牛顿法求隐含波动率 - 优化算法: 梯度下降迭代

列表 39.4
# =============================================================================
# 题目:使用while循环实现牛顿迭代法求平方根
# =============================================================================
# 牛顿法(Newton's Method)是一种强大的数值算法,用于求解方程的根
# 本任务演示while循环在迭代算法中的应用
# 金融应用:期权定价中的隐含波动率求解、债券收益率计算

import numpy as np  # 导入NumPy库

# ==================== 定义牛顿法求平方根函数 ====================
def sqrt_newton(x, tolerance=1e-6, max_iter=100):
    """
    使用牛顿迭代法计算平方根

    数学原理:
    求解方程 f(x) = x² - S = 0
    牛顿迭代公式:x_{n+1} = x_n - f(x_n) / f'(x_n)
    简化为:x_{n+1} = 0.5 * (x_n + S / x_n)

    参数:
        x (float): 初始猜测值(迭代起点)
        tolerance (float): 容差,控制精度(默认1e-6,即0.000001)
        max_iter (int): 最大迭代次数,防止无限循环(默认100次)

    返回:
        float: 平方根的近似值
    """
    # 定义要求根的数
    S = 2.0  # 目标:求√2的值

    # 初始化:设置当前近似值为初始猜测
    x_old = x  # x_old表示第n次迭代的值

    # ==================== 打印迭代过程表头 ====================
    print(f'牛顿法求√{S}:')
    # 打印表头:显示列名
    print(f'{"迭代":<6}{"近似值":<15}{"误差":<15}')
    # 打印分隔线(40个减号)
    print('-' * 40)

    # ==================== for循环:迭代求解 ====================
    # 注意:虽然用for循环,但内部有break语句
    # 实际应用中也可以用while循环:while error >= tolerance
    for i in range(max_iter):
        # ==================== 牛顿迭代公式 ====================
        # x_{n+1} = 0.5 × (x_n + S / x_n)
        # 直观理解:当前猜测和S/当前猜测的平均值
        x_new = 0.5 * (x_old + S / x_old)
        # 计算新的近似值

        # ==================== 计算误差 ====================
        # 绝对误差:|x_{n+1} - x_n|
        error = abs(x_new - x_old)
        # abs():绝对值函数

        # ==================== 打印当前迭代结果 ====================
        # {i+1}:迭代次数(从1开始计数)
        # {x_new:<15.10f}:近似值,宽度15,保留10位小数
        # {error:<15.10f}:误差,宽度15,保留10位小数
        print(f'{i+1:<6}{x_new:<15.10f}{error:<15.10f}')

        # ==================== 检查收敛条件 ====================
        # 如果误差小于容差,认为已收敛,停止迭代
        if error < tolerance:
            # 收敛成功:显示收敛信息
            print(f'\n收敛! 迭代{i+1}次后达到精度要求')
            # break语句:立即退出循环
            break  # 跳出for循环

        # ==================== 更新迭代值 ====================
        # 为下一次迭代做准备
        x_old = x_new
        # 将新值赋给旧值,继续下一次迭代

    # ==================== for-else结构 ====================
    # else子句:只有当for循环正常结束(未执行break)时才执行
    else:
        # 未收敛:达到最大迭代次数仍未达到精度要求
        print(f'\n警告: 未在{max_iter}次内收敛')
        # 可能原因:初始猜测值不佳、函数不收敛

    # ==================== 返回最终结果 ====================
    return x_new  # 返回平方根的近似值

# ==================== 调用牛顿法函数 ====================
# 从初始猜测值1.0开始迭代
sqrt_approx = sqrt_newton(1.0)
# 参数说明:1.0是初始猜测值,其他参数使用默认值

# 使用NumPy内置函数计算精确值(用于对比)
sqrt_actual = np.sqrt(2)
# np.sqrt():NumPy的平方根函数,使用高度优化的算法

# ==================== 结果对比与误差分析 ====================
print(f'\n近似值: {sqrt_approx:.10f}')
# 显示牛顿法计算的结果(10位小数)

print(f'实际值: {sqrt_actual:.10f}')
# 显示NumPy计算的结果(精确值)

# 计算并显示绝对误差
absolute_error = abs(sqrt_approx - sqrt_actual)
print(f'误差: {absolute_error:.2e}')
# {:.2e}:科学计数法,保留2位小数
# 例如:1.23e-06表示0.00000123

# ==================== 金融应用:隐含波动率计算 ====================
def implied_volatility(option_price, S, K, T, r=0.05, max_iter=100):
    """
    使用牛顿法求期权隐含波动率

    背景知识:
    Black-Scholes期权定价公式:
    C = S × N(d1) - K × e^(-rT) × N(d2)

    隐含波动率问题:
    已知市场期权价格,反推波动率参数σ
    这是无法用解析法求解的,必须使用数值方法

    牛顿迭代公式:
    σ_{n+1} = σ_n - (BS(σ_n) - MarketPrice) / Vega(σ_n)

    参数:
        option_price (float): 市场期权价格
        S (float): 标的资产当前价格
        K (float): 期权行权价
        T (float): 到期时间(年)
        r (float): 无风险利率,默认5%
        max_iter (int): 最大迭代次数

    返回:
        float: 隐含波动率的估计值
    """
    # ==================== 初始化波动率猜测 ====================
    sigma = 0.3  # 初始波动率猜测:30%(常见的股票波动率水平)

    # 定义收敛容差
    tolerance = 1e-6  # 精度要求:0.0001%

    # ==================== 牛顿迭代循环 ====================
    for i in range(max_iter):
        # 注意:这里是简化版演示
        # 实际应用需要实现完整的Black-Scholes公式和Vega计算

        # 完整版本应该包含:
        # price_bs = black_scholes(S, K, T, r, sigma)  # BS公式计算期权价格
        # vega = vega_greek(S, K, T, r, sigma)  # Vega:期权价格对波动率的导数
        # sigma_new = sigma - (price_bs - option_price) / vega  # 牛顿迭代公式

        # ==================== 简化版本(仅演示逻辑) ====================
        # 假设:期权价格与波动率成正比(线性近似)
        # 实际中这是非线性的,这里仅为演示
        price_diff = option_price - (S * 0.6)  # 价格差异
        sigma_new = sigma + price_diff * 0.1  # 调整波动率

        # ==================== 检查收敛 ====================
        # 如果波动率变化小于容差,认为已收敛
        if abs(sigma_new - sigma) < tolerance:
            break  # 退出循环

        # ==================== 更新波动率 ====================
        sigma = sigma_new  # 为下一次迭代做准备

    # ==================== 返回结果 ====================
    return sigma  # 返回隐含波动率

# ==================== 隐含波动率计算演示 ====================
print(f'\n隐含波动率计算:')
print(f'期权价格: 5.0元, 标的价格: 100元')

# 调用函数计算隐含波动率
# 参数:期权价格5.0元,标的价格100元,行权价105元,到期时间1年
iv = implied_volatility(5.0, 100, 105, 1)

# 显示结果
print(f'隐含波动率: {iv:.2%}')
# {:.2%}:百分比格式,保留2位小数
# 例如:0.2523显示为25.23%

代码深度解析:

  1. 牛顿法原理: \[ x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)} \]
    • 使用函数值和导数
    • 二阶收敛速度(每次迭代有效位数翻倍)
    • 适合光滑函数求根
  2. 收敛判断:
    • abs(x_new - x_old) < tolerance: 绝对误差
    • 或相对误差: abs(x_new - x_old) / abs(x_old) < tolerance
    • 避免无限循环
  3. 金融应用:隐含波动率:
    • 反问题: 从期权价格反推波动率
    • 无法用解析解,必须数值求解
    • 牛顿法是标准方法

39.3.3 循环控制语句

补充说明:break和continue

  • break: 立即退出循环
  • continue: 跳过本次迭代,继续下一次
列表 39.5
# =============================================================================
# 题目:演示循环控制语句break和continue的用法
# =============================================================================
# break和continue是控制循环执行流程的重要语句
# 金融应用:数据筛选、异常值处理、交易信号触发

# ==================== break语句演示 ====================
# break:立即退出循环,不再执行剩余的迭代

print('break演示(找到第一个正收益):')

# 定义收益率序列(包含正负收益)
returns = [-0.02, 0.03, -0.01, 0.04, 0.01, -0.03, 0.02]
# 场景:寻找第一个正收益的交易日

# enumerate()函数:同时获取索引和值
# enumerate(returns)生成:(0, -0.02), (1, 0.03), (2, -0.01), ...
for i, ret in enumerate(returns):
    # 判断:当前收益率是否为正
    if ret > 0:
        # 找到第一个正收益
        print(f'  第{i+1}天: {ret:.2%}')
        # break语句:立即退出for循环
        break  # 不再检查后续元素

# 注意:循环在第2天(索引1)就停止了
# 后续的天数(3-7天)都不会被访问

# ==================== continue语句演示 ====================
# continue:跳过本次迭代的剩余代码,继续下一次迭代

print('\ncontinue演示(跳过负收益):')

# 初始化计数器
positive_count = 0  # 用于统计正收益天数

# 遍历所有收益率
for ret in returns:
    # 判断:当前收益率是否为负
    if ret < 0:
        # 负收益:跳过本次迭代
        continue  # 直接进入下一次循环,不执行后面的代码

    # 只有执行到这里的才是正收益(因为负收益被continue跳过了)
    positive_count += 1  # 计数器加1
    print(f'  正收益: {ret:.2%}')
    # 显示正收益的值

# 显示统计结果
print(f'\n正收益天数: {positive_count}天')
# 输出:正收益的天数(不包含负收益)

# ==================== 嵌套循环与for-else结构 ====================
# 嵌套循环:循环内部还有循环
# for-else:else子句在循环正常结束时执行(未被break中断)

print('\n嵌套循环演示:')

# 外层循环:i从0到2
for i in range(3):
    # 内层循环:j从0到2
    for j in range(3):
        # 判断:是否在对角线上(i等于j)
        if i == j:
            # 对角线元素:显示
            print(f'  ({i}, {j}): 对角线元素')
        else:
            # 非对角线元素:跳过
            continue  # 跳过本次内层循环

    # ==================== for-else结构 ====================
    # 内层for循环的else子句
    # 只有当内层循环正常完成(未被break中断)时才执行
    else:
        print(f'第{i}行完成')
        # 这表示内层循环检查完了所有j值

# 输出解读:
# 第0行:(0,0)显示,(0,1)和(0,2)跳过,然后显示"第0行完成"
# 第1行:(1,1)显示,(1,0)和(1,2)跳过,然后显示"第1行完成"
# 第2行:(2,2)显示,(2,0)和(2,1)跳过,然后显示"第2行完成"

39.4 函数高级应用

39.4.1 可变参数与关键字参数

**理论背景:*args和kwargs的原理

Python支持可变参数,这是通过元组(tuple)和字典(dict)实现的:

  • *args: 收集所有位置参数为元组
  • **kwargs: 收集所有关键字参数为字典

函数签名示例:

def func(a, b, *args, **kwargs):
    pass

调用示例:

func(1, 2, 3, 4, x=5, y=6)
# a=1, b=2
# args=(3, 4)
# kwargs={'x': 5, 'y': 6}
列表 39.6
# =============================================================================
# 题目:实现现金流现值计算函数(使用可变参数)
# =============================================================================
# 现值(Present Value)是金融学的核心概念
# 公式:PV = Σ(CF_t / (1+r)^t),其中CF_t是第t期现金流,r是贴现率
# 本任务演示*args可变参数的使用
# 金融应用:投资决策、债券定价、项目评估

# ==================== 定义现值计算函数 ====================
def PV(R, *NCF, verbose=False):
    """
    计算现金流现值(Present Value)

    数学原理:
    PV = CF₁/(1+r)¹ + CF₂/(1+r)² + ... + CFₙ/(1+r)ⁿ
    其中CF_t是第t期现金流,r是贴现率

    参数:
        R (float): 贴现率(如0.05表示5%)
        *NCF (float): 可变数量的现金流(Net Cash Flow)
                     可以是任意数量的现金流
        verbose (bool): 是否显示详细计算过程(默认False)

    返回:
        float: 所有现金流的现值总和
    """
    # ==================== 初始化变量 ====================
    pv = 0  # 现值累加器,初始值为0
    n = 1  # 期数计数器,从第1期开始

    # ==================== 条件输出:是否显示详细过程 ====================
    # 如果verbose为True,显示计算过程表头
    print('现值计算过程:') if verbose else None
    # 三元表达式:条件为真时执行print,否则什么都不做(None)

    print('-' * 60) if verbose else None
    # 打印分隔线(60个减号)

    # ==================== for循环:遍历每期现金流 ====================
    # *NCF收集所有传入的现金流参数,形成元组
    for cf in NCF:
        # 计算折现因子(Discount Factor)
        # 公式:DF = 1 / (1+r)^n
        discount_factor = 1 / (1 + R) ** n
        # 折现因子表示未来1元在当前的价值

        # 计算当前现金流的现值
        # PV = CF × DF
        discounted_cf = cf * discount_factor
        # 将未来现金流折现到当前

        # 累加现值
        pv += discounted_cf
        # pv = pv + discounted_cf的简写形式

        # ==================== 条件输出:显示每期计算 ====================
        if verbose:
            # 格式化输出:显示期数、现金流、折现因子、现值
            print(f'  第{n}期: 现金流={cf:8.2f}, 折现因子={discount_factor:.6f}, 现值={discounted_cf:8.2f}')
            # {cf:8.2f}:宽度8,保留2位小数
            # {discount_factor:.6f}:保留6位小数

        # 期数递增
        n += 1  # 为下一期做准备

    # ==================== 条件输出:显示汇总结果 ====================
    if verbose:
        # 打印分隔线
        print('-' * 60)
        # 显示现值总和
        print(f'现值总和: {pv:.2f}元')

    # ==================== 返回最终结果 ====================
    return pv  # 返回现值总和

# ==================== 案例1:简单投资项目 ====================
print('案例1: 投资项目现金流')

# 定义现金流序列
cash_flows = [-10000, 8000, 12000]
# 解读:
# -10000:初始投资(第0年,负数表示流出)
# 8000:第1年回报(正数表示流入)
# 12000:第2年回报(正数表示流入)

# 调用PV函数计算净现值(NPV)
# *cash_flows:解包操作,将列表元素作为独立参数传递
# 等价于:PV(0.05, -10000, 8000, 12000)
pv1 = PV(0.05, *cash_flows)

# 显示结果
print(f'NPV(净现值): {pv1:.2f}\n')
# NPV > 0:项目创造价值,值得投资
# NPV < 0:项目破坏价值,不应投资

# ==================== 案例2:多期项目(详细输出) ====================
print('案例2: 多期项目')

# 定义5年期项目的现金流
project_flows = [
    -50000,  # 第0年:初始投资
    10000,   # 第1年:回报
    15000,   # 第2年:回报
    20000,   # 第3年:回报
    25000,   # 第4年:回报
    30000    # 第5年:回报
]

# 调用PV函数,设置verbose=True显示详细计算过程
pv2 = PV(0.08, *project_flows, verbose=True)
# 参数说明:
# 0.08:贴现率8%
# *project_flows:解包现金流列表
# verbose=True:显示详细计算过程

# ==================== 案例3:债券定价 ====================
print('\n案例3: 债券定价')

# 定义债券现金流
# 场景:10年期债券,面值1000元,票面利率5%(每年50元利息)
bond_flows = [50] * 9 + [1050]
# 解读:
# [50] * 9:前9年每年50元利息
# [1050]:第10年50元利息 + 1000元本金

# 调用PV函数计算债券现值(债券价格)
pv_bond = PV(0.06, *bond_flows)
# 参数说明:
# 0.06:市场收益率6%(贴现率)
# *bond_flows:解包债券现金流列表

# 显示债券定价结果
print(f'债券现值: {pv_bond:.2f}\n')
# 如果现值 > 面值(1000):债券溢价(市场利率 < 票面利率)
# 如果现值 < 面值(1000):债券折价(市场利率 > 票面利率)

代码深度解析:

  1. 折现公式: \[ PV = \sum_{t=1}^{n} \frac{CF_t}{(1+r)^t} \]

    • \(CF_t\): 第t期现金流
    • \(r\): 贴现率
    • \(t\): 时间期
  2. 净现值(NPV):

    • NPV > 0: 项目创造价值
    • NPV < 0: 项目破坏价值
    • NPV = 0: 盈亏平衡
  3. 不定参数的好处:

    • 函数通用性强
    • 可以处理任意数量的现金流
    • 金融应用:处理不同期限的债券

39.4.2 递归函数

理论背景:递归与数学归纳法

递归(Recursion)是函数调用自身的技术。

递归三要素: 1. 基准情形(Base Case): 递归终止条件 2. 递归关系(Recursive Relation): 问题如何分解 3. 收敛性: 确保递归最终会终止

数学归纳法: - 归纳基础: 验证n=1成立 - 归纳假设: 假设n=k成立 - 归纳步骤: 证明n=k+1也成立

列表 39.7
# =============================================================================
# 题目:演示递归函数在金融计算中的应用
# =============================================================================
# 递归(Recursion)是函数调用自身的技术
# 本任务包含三个案例:阶乘、二叉树期权定价、斐波那契数列
# 金融应用:期权定价、路径依赖期权、组合管理

import numpy as np  # 导入NumPy库,用于数值计算

# ==================== 案例1:递归计算阶乘 ====================
def factorial(n):
    """
    计算n的阶乘:n! = 1×2×3×...×n

    递归定义:
    - 基准情形:0! = 1, 1! = 1
    - 递归关系:n! = n × (n-1)!

    参数:
        n (int): 非负整数

    返回:
        int: n的阶乘值
    """
    # ==================== 基准情形(Base Case)====================
    # 递归终止条件:防止无限递归
    if n <= 1:
        # 0! = 1, 1! = 1(数学定义)
        return 1

    # ==================== 递归情形(Recursive Case)====================
    # 递归调用:函数调用自身
    # n! = n × (n-1)!
    return n * factorial(n - 1)
    # 执行过程(以factorial(3)为例):
    # 3! = 3 × 2!
    #    = 3 × (2 × 1!)
    #    = 3 × (2 × (1 × 0!))
    #    = 3 × (2 × (1 × 1))
    #    = 3 × 2 × 1 × 1
    #    = 6

# ==================== 测试阶乘函数 ====================
print('阶乘计算:')

# 定义测试数据:不同整数的阶乘
test_values = [1, 5, 10, 20]

# for循环:依次计算每个数的阶乘
for n in test_values:
    result = factorial(n)  # 调用递归函数
    print(f'{n}! = {result}')
    # 输出阶乘结果
    # 注意:20!已经是一个非常大的数(19位数字)

# ==================== 案例2:二叉树期权定价模型 ====================
def binomial_tree(S, K, T, r, sigma, n, call=True):
    """
    二叉树模型计算期权价格(Cox-Ross-Rubinstein模型)

    原理:
    将时间离散化为n期,每期股价上涨u倍或下跌d倍
    使用风险中性定价理论计算期权现值

    参数:
        S (float): 标的资产当前价格
        K (float): 期权行权价
        T (float): 到期时间(年)
        r (float): 无风险利率(如0.05表示5%)
        sigma (float): 波动率
        n (int): 二叉树步数(更多步数=更精确)
        call (bool): True=看涨期权, False=看跌期权

    返回:
        float: 期权价格
    """
    # ==================== 第一步:计算模型参数 ====================
    # 时间步长:将总时间T分为n等份
    dt = T / n
    # dt:每期的时间长度

    # 上涨因子:u = e^(σ√dt)
    u = np.exp(sigma * np.sqrt(dt))
    # 股价上涨的倍数

    # 下跌因子:d = 1/u
    d = 1 / u
    # 股价下跌的倍数(保证树的重构特性)

    # 风险中性概率:p = (e^(r×dt) - d) / (u - d)
    p = (np.exp(r * dt) - d) / (u - d)
    # 股价上涨的概率(在风险中性世界中)

    # ==================== 第二步:迭代法(动态规划)计算期权价值 ====================
    # 注:纯递归在n较大时计算量为O(2^n),会导致计算时间爆炸
    # 采用从终端节点向前回推的迭代法,复杂度仅为O(n^2)

    # 初始化终端节点(到期日)的期权价值数组
    option_values = np.zeros(n + 1)  # n+1个终端节点

    for j in range(n + 1):  # 遍历每个终端节点
        # 计算终端股价:S × u^j × d^(n-j)
        S_T = S * (u ** j) * (d ** (n - j))  # j次上涨,(n-j)次下跌

        if call:  # 看涨期权
            option_values[j] = max(S_T - K, 0)  # 内在价值 = max(S_T - K, 0)
        else:  # 看跌期权
            option_values[j] = max(K - S_T, 0)  # 内在价值 = max(K - S_T, 0)

    # 从终端节点向根节点回推
    for i in range(n - 1, -1, -1):  # 从第n-1期回推到第0期
        for j in range(i + 1):  # 每期有i+1个节点
            # 风险中性定价公式:V = e^(-r×dt) × [p×V_up + (1-p)×V_down]
            option_values[j] = np.exp(-r * dt) * (
                p * option_values[j + 1] + (1 - p) * option_values[j]
            )  # 折现后的期望价值

    # 根节点即为期权当前价格
    option_price = option_values[0]  # 第0期第0个节点的值

    # 返回期权价格
    return option_price

# ==================== 测试二叉树模型 ====================
# 定义期权参数
S = 100  # 标的资产价格:100元
K = 105  # 行权价:105元
T = 1    # 到期时间:1年
r = 0.05 # 无风险利率:5%
sigma = 0.2  # 波动率:20%
# 注:生产环境中可使用n=100或更高步数以提升精度
n = 20  # 二叉树步数:20步(教学演示用,减少计算时间)

# 调用二叉树函数计算看涨期权价格
option_price = binomial_tree(S, K, T, r, sigma, n)
print(f'\n二叉树模型计算的期权价格: {option_price:.2f}元')
# 输出:期权的公平价值

# ==================== 案例3:斐波那契数列(递归演示) ====================
def fibonacci(n):
    """
    计算斐波那契数列的第n项

    递归定义:
    - F(0) = 0
    - F(1) = 1
    - F(n) = F(n-1) + F(n-2)

    应用:斐波那契数列在金融技术分析中用于预测支撑阻力位

    参数:
        n (int): 非负整数

    返回:
        int: 斐波那契数列的第n项
    """
    # ==================== 基准情形 ====================
    if n <= 1:
        # F(0) = 0, F(1) = 1
        return n

    # ==================== 递归情形 ====================
    # F(n) = F(n-1) + F(n-2)
    return fibonacci(n - 1) + fibonacci(n - 2)

# ==================== 测试斐波那契函数 ====================
print(f'\n斐波那契数列前10项:')

# 使用列表推导式生成前10项
fib_sequence = [fibonacci(i) for i in range(10)]
# [fibonacci(0), fibonacci(1), ..., fibonacci(9)]
# 结果:[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

# 显示斐波那契数列
print(fib_sequence)

代码深度解析:

  1. 二叉树模型原理:
    • 将时间离散化为n期
    • 每期价格上涨u倍或下跌d倍
    • 风险中性定价: 期望收益率=无风险利率
  2. CRR模型参数:
    • \(u = e^{\sigma \sqrt{\Delta t}}\)
    • \(d = 1/u\)
    • \(p = \frac{e^{r\Delta t} - d}{u - d}\)
  3. 递归的效率问题:
    • 朴素递归: \(O(2^n)\) (指数级)
    • 动态规划: \(O(n^2)\) (平方级)
    • 金融应用中必须优化

39.4.3 函数作用域

理论背景:LEGB规则

Python变量查找遵循LEGB作用域层次: 1. L(Local): 局部作用域(函数内) 2. E(Enclosing): 闭包作用域(嵌套函数) 3. G(Global): 模块作用域(文件内) 4. B(Built-in): 内置作用域(Python内置)

列表 39.8
# =============================================================================
# 题目:演示Python变量作用域与LEGB规则
# =============================================================================
# 变量作用域决定了变量在程序中的可见范围
# Python使用LEGB规则查找变量:Local -> Enclosing -> Global -> Built-in
# 金融应用:避免全局变量污染、函数封装、参数传递

# ==================== 定义全局变量 ====================
# 全局变量(Global Scope):在模块级别定义的变量
x = 10  # 全局变量x,值为10
y = 20  # 全局变量y,值为20

# ==================== 演示1:局部变量遮蔽全局变量 ====================
def func_local():
    """
    演示局部变量如何遮蔽全局变量

    作用域规则:
    - 函数内定义的变量是局部变量
    - 局部变量与全局变量同名时,局部变量优先(遮蔽全局变量)
    """
    # 在函数内定义同名变量x
    x = 5  # 这是局部变量,不是全局变量x
    # 此时有两个x变量:
    # - 全局变量x(值为10),在函数外
    # - 局部变量x(值为5),在函数内

    # 访问的是局部变量x(遮蔽效应)
    print(f'函数内x: {x} (局部)')

# ==================== 演示2:使用global修改全局变量 ====================
def func_global():
    """
    演示如何使用global关键字修改全局变量

    global关键字:
    - 声明要修改的是全局变量,而不是创建局部变量
    - 不使用global,赋值会创建局部变量
    """
    # 声明使用全局变量y
    global y  # 告诉Python:这里的y是全局变量y

    # 修改全局变量y
    y = 50  # 现在修改的是全局变量y,不是创建局部变量

    # 访问全局变量y
    print(f'函数内y: {y} (修改后的全局)')

# ==================== 演示3:闭包作用域(Enclosing)====================
def func_enclosing():
    """
    演示嵌套函数中的变量作用域

    闭包(Closure):
    - 内层函数可以访问外层函数的变量
    - 即使外层函数执行完毕,变量仍被保留
    """
    # 外层函数的局部变量
    z = 100  # 这是func_enclosing的局部变量

    # 定义内层函数
    def inner():
        """
        内层函数:访问外层函数的变量
        """
        # 访问x:全局变量
        # 访问z:外层函数的变量(Enclosing作用域)
        return x + z

    # 调用内层函数
    return inner()  # 返回内层函数的执行结果

# ==================== 执行函数并观察变量变化 ====================
# 显示初始状态
print('初始状态:')
print(f'全局x: {x}, 全局y: {y}')
# 输出:全局x=10, 全局y=20

# 调用func_local(演示局部变量遮蔽)
func_local()
# 函数内打印:函数内x: 5 (局部)

print(f'func_local()后, 全局x: {x} (未改变)')
# 全局变量x仍然是10,没有被修改
# 因为func_local中的x是局部变量,不影响全局变量

# 调用func_global(演示修改全局变量)
func_global()
# 函数内打印:函数内y: 50 (修改后的全局)

print(f'func_global()后, 全局y: {y} (已修改)')
# 全局变量y已经被修改为50
# 因为func_global使用了global关键字

# 调用func_enclosing(演示闭包)
closure_result = func_enclosing()
print(f'闭包结果: {closure_result}')
# 输出:闭包结果: 110(全局x=10 + 外层z=100)

# ==================== 演示4:忘记使用global的错误 ====================
def modify_global_wrong():
    """
    错误示例:试图修改全局变量但忘记使用global

    常见错误:
    - 在函数内直接赋值全局变量
    - 没有使用global关键字
    - 结果是创建了局部变量,而不是修改全局变量
    """
    # 注意:这里没有"global y"声明
    # global y  # 如果没有这行,会创建局部变量

    y = 30  # 这会创建局部变量y,而不是修改全局y
    # 此时有两个y变量:
    # - 全局变量y(值为50)
    # - 局部变量y(值为30),只在函数内有效

# ==================== 验证错误示例 ====================
print(f'\n错误示例测试:')
print(f'修改前全局y: {y}')
# 输出:全局y = 50

modify_global_wrong()
# 函数执行:创建了局部变量y=30

print(f'modify_global_wrong()后, 全局y: {y} (未修改,因为没有global)')
# 输出:全局y = 50(未被修改)
# 因为modify_global_wrong中的y是局部变量

代码深度解析:

  1. global关键字:
    • 声明要修改全局变量
    • 不声明则创建局部变量
    • 不推荐使用全局变量(代码维护困难)
  2. 闭包(Closure):
    • 内层函数引用外层函数的变量
    • 即使外层函数返回,变量仍被保留
    • 金融应用:回调函数、策略参数
  3. 作用域最佳实践:
    • 尽量使用局部变量
    • 通过参数传递数据
    • 避免globalnonlocal(除非必要)

39.5 lambda表达式

理论背景:匿名函数与函数式编程

lambda表达式源自λ演算(Lambda Calculus),由Alonzo Church在1930年代发明。

语法: lambda parameters: expression

特点: - 匿名: 不需要函数名 - 简洁: 单行表达式 - 函数式: 可作为参数传递

金融应用: - 量化策略的快速定义 - 数据清洗的转换函数 - 回调函数

列表 39.9
# =============================================================================
# 题目:演示lambda表达式在金融计算中的应用
# =============================================================================
# lambda(匿名函数)是简洁的单行函数定义方式
# 语法:lambda parameters: expression
# 金融应用:快速定义计算逻辑、高阶函数参数、回调函数

# ==================== 案例1:简单数学函数 ====================
# lambda语法:lambda 参数列表: 表达式
# 特点:单个表达式,自动返回结果,无需return语句

# 定义平方函数(lambda版本)
square = lambda x: x**2
# 解读:
# - lambda x:定义参数x
# - x**2:计算x的平方(返回值)
# - square:将lambda函数赋值给变量square

# 定义立方函数(lambda版本)
cube = lambda x: x**3
# 计算x的三次方

# ==================== 测试简单函数 ====================
print('lambda函数演示:')

# 调用square函数
print(f'平方: {square(5)} = {square(5)}')
# 输出:平方: 25(5²)

# 调用cube函数
print(f'立方: {cube(5)} = {cube(5)}')
# 输出:立方: 125(5³)

# ==================== 案例2:金融收益率计算 ====================
# 定义简单收益率函数
simple_return = lambda initial, final: (final - initial) / initial
# 参数:
# - initial:初始价格
# - final:最终价格
# 公式:(最终价格 - 初始价格) / 初始价格

# 定义复利收益函数
compound_return = lambda initial, rate, periods: initial * (1 + rate) ** periods - initial
# 参数:
# - initial:初始本金
# - rate:每期收益率
# - periods:期数
# 公式:初始本金 × (1+收益率)^期数 - 初始本金

# ==================== 测试收益率函数 ====================
print(f'\n简单收益率: {simple_return(100, 110):.2%}')
# 场景:价格从100涨到110
# 输出:10.00%((110-100)/100 = 0.10)

print(f'复利收益: {compound_return(10000, 0.05, 10):.2f}')
# 场景:10000元,5%年利率,10年复利
# 输出:6288.95元(复利增长)

# ==================== 案例3:多参数lambda - 投资组合净值 ====================
# 定义投资组合净值计算函数
net_value = lambda prices, shares: sum(p * s for p, s in zip(prices, shares))
# 参数:
# - prices:股票价格列表
# - shares:持股数量列表
# 逻辑:
# - zip(prices, shares):将价格和数量配对
# - p * s for p, s in ...:计算每只股票的市值
# - sum(...):求和得到总净值

# 定义测试数据
prices = [10.5, 22.3, 15.8]  # 三只股票的价格
shares = [100, 200, 150]  # 对应的持股数量

# 计算投资组合净值
value = net_value(prices, shares)
print(f'\n投资组合净值: {value:.2f}元')
# 计算:
# - 股票1:10.5 × 100 = 1050元
# - 股票2:22.3 × 200 = 4460元
# - 股票3:15.8 × 150 = 2370元
# - 总净值:1050 + 4460 + 2370 = 7880元

# ==================== 案例4:lambda与高阶函数 - map ====================
# 高阶函数:接收函数作为参数的函数

# 定义股票列表
stocks = ['贵州茅台', '五粮液', '招商银行']
prices = [1850, 220, 45]

# 使用map()应用lambda函数
# map(function, iterable):对可迭代对象的每个元素应用函数
formatted = list(map(lambda x: f'{x}元', prices))
# lambda x: f'{x}元':将数字格式化为"数字+元"的字符串
# list(...):将map对象转换为列表

print(f'\n格式化价格: {formatted}')
# 输出:['1850元', '220元', '45元']

# ==================== 案例5:lambda与高阶函数 - filter ====================
# filter(function, iterable):过滤符合条件的元素

# 使用filter筛选高价股票
expensive_stocks = list(filter(lambda p: p[1] > 200, zip(stocks, prices)))
# lambda p: p[1] > 200:筛选条件(价格>200)
# zip(stocks, prices):将股票名和价格配对
# p[1]:取元组的第二个元素(价格)

print(f'高价股票: {[s + "=" + str(p) + "元" for s, p in expensive_stocks]}')
# 输出:['贵州茅台=1850元', '五粮液=220元']

# ==================== 案例6:lambda与高阶函数 - sorted ====================
# sorted(iterable, key=function):使用自定义函数排序

# 定义投资组合(字典列表)
portfolio = [
    {'name': '贵州茅台', 'return': 0.15},
    {'name': '五粮液', 'return': 0.08},
    {'name': '招商银行', 'return': 0.12}
]

# 按收益率降序排列
sorted_portfolio = sorted(portfolio, key=lambda x: x['return'], reverse=True)
# lambda x: x['return']:提取收益率作为排序依据
# reverse=True:降序排列(从高到低)

print(f'\n按收益率排序:')
for stock in sorted_portfolio:
    print(f"  {stock['name']}: {stock['return']:.2%}")
# 输出:
# 贵州茅台: 15.00%
# 招商银行: 12.00%
# 五粮液: 8.00%

# ==================== 案例7:lambda与条件表达式 ====================
# lambda函数可以使用条件表达式(三元表达式)

# 定义交易信号生成函数
get_signal = lambda r: 'BUY' if r > 0.05 else ('SELL' if r < -0.02 else 'HOLD')
# 逻辑:
# - r > 0.05:收益率>5%,买入
# - r < -0.02:收益率<-2%,卖出
# - 其他:观望

# 嵌套三元表达式:
# 'BUY' if condition1 else ('SELL' if condition2 else 'HOLD')

# 定义测试收益率序列
returns = [0.06, -0.03, 0.02, -0.01, 0.08]

# 使用列表推导式生成交易信号
signals = [get_signal(r) for r in returns]

# 显示交易信号
print(f'\n交易信号:')
for r, s in zip(returns, signals):
    print(f'  收益率={r:6.2%}: 信号={s}')
# 输出:
# 收益率= 6.00%: 信号=BUY(>5%)
# 收益率=-3.00%: 信号=SELL(<-2%)
# 收益率= 2.00%: 信号=HOLD(在-2%到5%之间)
# 收益率=-1.00%: 信号=HOLD
# 收益率= 8.00%: 信号=BUY

代码深度解析:

  1. lambda语法:

    lambda args: expression
    # 只能是单个表达式,不能是语句块
    # 自动返回表达式的值
  2. 何时使用lambda:

    • 适合: 简单单行函数
    • 适合: 作为参数传递给高阶函数
    • 不适合: 复杂逻辑(用def定义)
  3. 高阶函数:

    • map(func, iterable): 对每个元素应用函数
    • filter(func, iterable): 过滤元素
    • sorted(iterable, key=func): 自定义排序

平台任务1解答代码

以下代码与教学平台任务要求完全一致:

列表 39.10
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
#题目一
salary = 8000  # 基本工资
if salary <= 5000:  # 判断基本工资是否小于等于5000
    rate = 0  # 基本工资小于等于5000,不扣税,即扣税率为0%
else:  # 不满足以上条件时
    rate = 0.05  # 基本工资大于5000,扣税率为0.05,即5%

# 计算税后工资,基本工资-扣税,扣税额为超过3000部分的乘以扣税率
salary = salary - (salary - 5000) * rate
print("税后工资为:%d" % salary)  # 将税后工资打印输出

平台任务2解答代码

以下代码与教学平台任务要求完全一致:

列表 39.11
# 注:该代码块包含input()交互输入,渲染时无法执行
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
#题目二
sum_salary = 0
salary_list = []  # 定义列表salary_list
salary = 0  # 初始化薪资变量为0
while True:  # 条件循环
  salary = input('请输入员工的薪资,输入Q结束计算:')  # 获取用户键盘输入
  if salary.upper() == 'Q':  # 条件判断:salary.upper() == 'Q'
    print('程序结束')  # 输出程序结束
    break  # 跳出循环
  elif int(salary) <= 0:  # 否则判断:int(salary) <= 0
    print('您输入的数值有误,请重新输入')  # 输出您输入的数值有误,请重新输入
    continue  # 跳过本次迭代
    salary_list.append(salary)  # 将有效薪资数据添加到列表
  sum_salary += int(salary)  # 更新sum_salary的值
print(len(salary_list))  # 输出数据长度
print(sum_salary)  # 输出薪资数据

平台任务3解答代码

以下代码与教学平台任务要求完全一致:

列表 39.12
# 注:该代码块包含缩进错误的填空代码,需要在平台上完成
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
#题目三
Ri=[0.15,0.20,-0.10,0.35]        #收益率序列
Ai=[0.3,0.2,0.3,0.2]             #权重序列
TR=0  # 初始化投资组合期望报酬率为0
for j in range(len(Ri)):  # 遍历range(len(Ri))中的每个j
        TRij=Ri[j]*Ai[j]  # 计算第j项资产的加权收益贡献(收益率×权重)
    TR+=TRij  # 更新TR的值
print("投资组合的期望报酬率为:{:.2f}".format(TR))  # 输出投资组合的期望报酬率为:{:.2f}